home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NOVA - For the NeXT Workstation
/
NOVA - For the NeXT Workstation.iso
/
SourceCode
/
AdobeExamples
/
NX_ImportAdv
/
DrawingView.m
< prev
next >
Wrap
Text File
|
1992-12-19
|
51KB
|
2,129 lines
/*
* (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
*
* (b) If this Sample Code is distributed as part of the Display PostScript
* System Software Development Kit from Adobe Systems Incorporated,
* then this copy is designated as Development Software and its use is
* subject to the terms of the License Agreement attached to such Kit.
*
* (c) If this Sample Code is distributed independently, then the following
* terms apply:
*
* (d) This file may be freely copied and redistributed as long as:
* 1) Parts (a), (d), (e) and (f) continue to be included in the file,
* 2) If the file has been modified in any way, a notice of such
* modification is conspicuously indicated.
*
* (e) PostScript, Display PostScript, and Adobe are registered trademarks of
* Adobe Systems Incorporated.
*
* (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
* CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
* AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
* ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
* OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
* WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
* WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
* DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
* OF THIRD PARTY RIGHTS.
*/
/*
* DrawingView.m
*
* This view represents the page that the image is drawn onto. It is
* a subview of the DocView. The DocView is the document view of
* the ClipView. This view's real size grows with the scale but the
* bounds always stays the same.
*
* An offscreen buffer, bufferId, is used to draw into and then
* this buffer is composited onscreen. This technique allows for the
* separation of the static drawing from the temporal drawing (modal
* loop redrawing and control point display). The static drawing takes
* place in the buffer while the temporal drawing takes place in this view.
* The static drawing is stuff that is complex and that will stay around for
* a while. Saving this drawing in a buffer elimates having to redraw it
* for something simple like drawing a control point or something.
* (The bufferId is the content view of a plain window the size of the
* content view of this view's window.)
*
* Version: 2.0
* Author: Ken Fromm
* History:
* 03-17-91 Added this comment and fixed the preview section.
*/
#import "DrawingView.h"
#import "DocView.h"
#import "Document.h"
#import "GraphicImport.h"
#import "GraphicList.h"
#import "ImportPanel.h"
#import "SaveAsPanel.h"
#import "epsf.h"
#import "DrawingViewWraps.h"
#import <appkit/Cell.h>
#import <appkit/ClipView.h>
#import <appkit/NXCursor.h>
#import <appkit/NXImage.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Text.h>
#import <appkit/Pasteboard.h>
#import <appkit/PrintInfo.h>
#import <appkit/nextstd.h>
#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>
#import <math.h>
extern char ControlFont[ ];
extern const NXRect DefaultContentRect;
static char EpsfPboard[] = "Epsf Pasteboard Type";
static char EpsfProcSet[] = "EPSF_Illustrator_abbrev 0 0";
/*
* Timers used to automatically scroll when the mouse is
* outside the drawing view and not moving.
*/
static void startTimer(NXTrackingTimer ** timer, int *timermask, id window)
{
if (!*timer)
{
*timer = NXBeginTimer(NULL, 0.15, 0.2);
*timermask = NX_TIMERMASK;
[window addToEventMask:NX_TIMERMASK];
}
}
static void stopTimer(NXTrackingTimer **timer, int *timermask, id window)
{
if (*timer)
{
NXEndTimer(*timer);
*timer = NULL;
*timermask = 0;
[window removeFromEventMask:NX_TIMERMASK];
}
}
void compositeBuffer(int gstate, const NXRect *srce, const NXPoint *dest, int op)
{
PScomposite(NX_X(srce), NX_Y(srce), NX_WIDTH(srce), NX_HEIGHT(srce),
gstate, dest->x, dest->y, op);
}
/*
* The next two procedures are for the drawing buffer.
* This buffer is used to prevent unnecessary drawing by
* retaining images within offscreen windows.
*
* Create a plain window the size of the rectangle passed in and
* then insert a view into the window as a subview. A clip view
* is swapped for the content view so the buffer can be scrolled
* when this view is scrolled.
*/
static id createBuffer(const NXSize *winSize)
{
id buffer, clipview, window;
NXRect contRect;
contRect.origin.x = contRect.origin.y = 0;
contRect.size = *winSize;
window = [Window newContent:&contRect
style:NX_PLAINSTYLE
backing:NX_RETAINED
buttonMask:0
defer:NO] ;
buffer = [[[[View newFrame:&contRect] allocateGState] setFlipped:NO] setClipping:NO];
clipview = [[[ClipView new] setFlipped:NO] setDisplayOnScroll:NO];
[[window setContentView:clipview ] free];
[clipview setDocView:buffer];
[clipview allocateGState];
[window display];
return buffer;
}
/*
* Resize the buffer and its window. Called when this view's
* window resizes. (The buffer is enlarged by a bit as an
* added insurance.)
*/
void resizeBuffer(id buffer, const NXSize*newSize)
{
NXRect frameRect, contRect;
[buffer getFrame:&frameRect];
if (newSize->width > frameRect.size.width || newSize->height > frameRect.size.height)
{
contRect.origin.x = contRect.origin.y = 0.0;
contRect.size = *newSize;
NXInsetRect(&contRect, -10.0, -10.0);
[Window getFrameRect:&frameRect
forContentRect:&contRect
style:NX_PLAINSTYLE];
[[buffer window] sizeWindow:frameRect.size.width :frameRect.size.height];
[buffer sizeTo:newSize->width :newSize->height];
}
}
@implementation DrawingView
+newFrame:(const NXRect *) frameRect
{
self = [super newFrame:frameRect];
[[self allocateGState] setClipping:NO];
graphiclistId = [GraphicList new];
selectedlistId = [GraphicList new];
hitPoint = [NXApp hitPoint];
upathBuffer = [NXApp upathBuffer];
bufferId = createBuffer(&DefaultContentRect.size);
return self;
}
/* Free any unplaced imported object. */
- free
{
[graphicId free];
[[graphiclistId freeObjects] free];
[selectedlistId free];
[[bufferId window] free];
return [super free];
}
/*
* Aids for understanding the operation of the application.
* Moves the offscreen buffer onscreen.
*/
- showBuffer:sender
{
[[bufferId window] moveTo:10 :10];
[[bufferId window] orderFront:self];
[window orderFront:self];
return self;
}
- hideBuffer:sender
{
[[bufferId window] orderOut:self];
return self;
}
/*
* Returns the size of the control point scaled to reflect the current scale.
*/
- (float) controlPointSize
{
return FONTSIZE * (1.0/[superview scale]);
}
/*
* Scale the hit setting. Using an unscaled hit setting would be like
* using a boxing glove on a 400% scale.
*/
- (float) hitSetting
{
return ([NXApp hitSetting] * (1.0/[superview scale]));
}
- buffer
{
return bufferId;
}
- image
{
return imageId;
}
- setDirty:(BOOL) flag
{
dirty = flag;
[window setDocEdited:flag];
return self;
}
- (BOOL)isDirty
{
return dirty;
}
- (BOOL)isEmpty
{
return [graphiclistId count] == 0;
}
- (BOOL)isSelected
{
return [selectedlistId count] > 0;
}
/*
* Set the cursor to be the intersection of the bounds
* and the visible portion of this view.
*/
- resetCursorRects
{
NXRect visRect;
[self getVisibleRect:&visRect];
NXIntersectionRect(&bounds, &visRect);
[self addCursorRect:&visRect cursor:[NXApp cursor]];
return self;
}
/*
* When the drawing view moves, then move bufferId so that
* the composites from bufferId are taken from the correct spot.
*/
- moveTo:(NXCoord)x :(NXCoord)y
{
[super moveTo:x :y];
[bufferId moveTo:x :y];
return self;
}
/*
* A scale as well needs to be reflected in the offscreen buffer.
*/
- scale:(NXCoord)x :(NXCoord)y
{
[super scale:x :y];
[bufferId scale:x :y];
return self;
}
/*
* This method copies the PostScript code for the graphics and writes it to the
* stream passed in. Includes the preview image when appropriate.
*/
- writePSToStream:(NXStream *) stream
{
id nximageId, templist;
NXRect bbox;
if (stream && [selectedlistId count])
{
nximageId = NULL;
imageId = NULL;
templist = graphiclistId;
graphiclistId = selectedlistId;
[graphiclistId getBounds:&bbox];
if ([[SaveAsPanel new] format] == SAVE_EPSPREVIEW)
{
nximageId = [[NXImage alloc] initSize:&bbox.size];
[nximageId useCacheWithDepth:NX_TwoBitGrayDepth];
if ([nximageId lockFocus])
{
PStranslate(-bbox.origin.x, -bbox.origin.y);
PSsetgray(NX_WHITE);
NXRectFill(&bbox);
[graphiclistId drawObject:&bbox withFlags:NOFLAGS inView:self];
imageId = [[NXBitmapImageRep alloc] initData:NULL fromRect:&bbox];
[nximageId unlockFocus];
}
}
[self copyPSCodeInside:&bbox to:stream];
selectedlistId = graphiclistId;
graphiclistId = templist;
[nximageId free];
[imageId free];
imageId = NULL;
}
return self;
}
/* Pasteboard-related target/action methods */
/* Calls copy:, then removes and frees the objects in the selection. */
- cut:sender
{
float knobsize;
NXRect rect;
if ([selectedlistId count])
{
knobsize = -[self controlPointSize]/2;
[selectedlistId getBounds:&rect];
NXInsetRect(&rect, knobsize, knobsize);
if (sender != self)
[self copy:sender];
[graphiclistId removeObjectsIn:selectedlistId];
[selectedlistId freeObjects];
[self display:&rect :1];
[self setDirty:YES];
return self;
}
return nil;
}
/* Calls cut bypassing the copy. */
- delete:sender
{
return [self cut:self];
}
/*
* Puts all the objects in the selected list into the Pasteboard by
* archiving the list. See the draw program for placing the objects
* in the Pasteboard as a PostScript image. Much of the work is
* available already. The right hooks just have to be added.
*/
- copy:sender
{
BOOL error = YES;
id pasteboardId = [Pasteboard new];
char *dataptr;
const char *types[1];
int length, maxlen;
NXStream *stream;
NXTypedStream *ts;
if ([selectedlistId count])
{
types[0] = EpsfPboard;
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
if (stream)
{
ts = NXOpenTypedStream(stream, NX_WRITEONLY);
if (ts)
{
NXWriteRootObject(ts, selectedlistId);
NXCloseTypedStream(ts);
NXGetMemoryBuffer(stream, &dataptr, &length, &maxlen);
[pasteboardId declareTypes:types num:2 owner:[self class]];
[pasteboardId writeType:EpsfPboard data:dataptr length:length];
error = NO;
}
NXCloseMemory(stream, NX_FREEBUFFER);
}
}
return (error ? nil : self);
}
/* Routines to check the types in the Pasteboard */
static BOOL matchPasteType(char *const *types, const char *type)
{
if (types)
while (*types)
if (!strcmp(*types++, type))
return YES;
return NO;
}
/*
* Validates the pasteboard types and returns the preferred one.
*/
static const char *drawPasteType(char *const *types)
{
if (matchPasteType(types, EpsfPboard))
return EpsfPboard;
if (matchPasteType(types, NXPostScriptPboard))
return NXPostScriptPboard;
if (matchPasteType(types, NXTIFFPboardType))
return NXTIFFPboardType;
return NULL;
}
/*
* Pastes any type available from the Pasteboard into the DrawingView.
* If the type in the Pasteboard is the internal type, then the objects
* are simply added to the selected list and graphic list. If it is of PostScript
* or Tiff type then create a GraphicImport object using the contents of the
* Pasteboard.
*/
- paste:sender
{
id objectId,
pastelistId,
pasteboardId = [Pasteboard new];
char *dataptr;
const char *type;
int length;
float knobsize;
NXPoint offset;
NXRect drawRect, visRect;
NXStream *stream;
NXTypedStream *ts;
type = drawPasteType([pasteboardId types]);
if (type)
{
[self lockFocus];
[self deselectObject:selectedlistId];
[self unlockFocus];
[pasteboardId readType:type data:&dataptr length:&length];
stream = NXOpenMemory(dataptr, length, NX_READONLY);
if (strcmp(type, EpsfPboard) == 0)
{
ts = NXOpenTypedStream(stream, NX_READONLY);
pastelistId = NXReadObject(ts);
if ([pastelistId count])
{
[graphiclistId insertObjectsIn:pastelistId];
[selectedlistId insertObjectsIn:pastelistId];
[selectedlistId setSelected:YES];
}
NXCloseTypedStream(ts);
}
else if (strcmp(type, NXPostScriptPboard) == 0 ||
strcmp(type, NXTIFFPboardType) == 0) {
objectId = [[GraphicImport alloc] initFromStream:stream];
[graphiclistId addObject:objectId];
[selectedlistId addObject:objectId];
[selectedlistId setSelected:YES];
}
NXCloseMemory(stream, NX_SAVEBUFFER);
if ([selectedlistId count])
{
knobsize = -[self controlPointSize]/2;
[selectedlistId getBounds:&drawRect];
NXInsetRect(&drawRect, knobsize, knobsize);
[self getVisibleRect:&visRect];
offset.x = (visRect.origin.x + visRect.size.width/2.0) -
(drawRect.origin.x + drawRect.size.width/2.0);
offset.y = (visRect.origin.y + visRect.size.height/2.0) -
(drawRect.origin.y + drawRect.size.height/2.0);
[selectedlistId moveAll:&offset];
drawRect.origin.x += offset.x;
drawRect.origin.y += offset.y;
[self display:&drawRect :1];
[self setDirty:YES];
}
}
else
return nil;
return self;
}
/*
* Selects all the items in the graphiclistId.
*/
- selectAll:sender
{
int i;
float knobsize;
NXRect rect;
i = [graphiclistId count];
if (i && i > [selectedlistId count])
{
[selectedlistId free];
selectedlistId = [graphiclistId copy];
[selectedlistId setSelected:YES];
knobsize = -[self controlPointSize]/2;
[selectedlistId getBounds:&rect];
NXInsetRect(&rect, knobsize, knobsize);
[self lockFocus];
[self drawControl:selectedlistId forRect:&rect withFlags:NOFLAGS];
[self unlockFocus];
[window flushWindow];
}
return self;
}
/*
* Brings each of the items in the selected list to the front of the
* graphics list. The first item selected will be at the front of the
* graphics list with the others following immediately behind.
*/
- bringToFront:sender
{
id object;
BOOL change = NO;
int i, j, index;
float knobsize;
NXRect rect;
j = [selectedlistId count];
for (i = 0; i < j; i++)
{
object = [selectedlistId objectAt:i];
index = [graphiclistId indexOf:object];
if (index != i)
{
[graphiclistId removeObjectAt:index];
[graphiclistId insertObject:object at:i];
change = YES;
}
}
if (change)
{
knobsize = -[self controlPointSize]/2;
[selectedlistId getBounds:&rect];
NXInsetRect(&rect, knobsize, knobsize);
[self display:&rect :1];
[self setDirty:YES];
}
return self;
}
/*
* Moves each of the items in the selected list to the back of the
* graphics list. The last item selected will be at the back of the
* graphics list with the others following immediately ahead.
*/
- sendToBack:sender
{
id object;
BOOL change = NO;
int numS, numG, i, index;
float knobsize;
NXRect rect;
numS = [selectedlistId count] - 1;
numG = [graphiclistId count] - 1;
for (i = numS; i >= 0; i--)
{
object = [selectedlistId objectAt:i];
index = [graphiclistId indexOf:object];
if (numG - index != numS - i)
{
[graphiclistId removeObjectAt:index];
[graphiclistId insertObject:object at:(numG - (numS - i))];
change = YES;
}
}
if (change)
{
knobsize = -[self controlPointSize]/2;
[selectedlistId getBounds:&rect];
NXInsetRect(&rect, knobsize, knobsize);
[self display:&rect :1];
[self setDirty:YES];
}
return self;
}
- makeOriginalUsing:(SEL) aMethod
{
float knobsize;
NXRect rect_start, rect_end;
knobsize = -[self controlPointSize]/2;
[selectedlistId getBounds:&rect_start];
NXInsetRect(&rect_start, knobsize, knobsize);
if ([selectedlistId makeObjectsPerform:aMethod])
{
[selectedlistId getBounds:&rect_end];
NXInsetRect(&rect_end, knobsize, knobsize);
NXUnionRect(&rect_start, &rect_end);
[self lockFocus];
[self drawSelf:&rect_end :1];
[self unlockFocus];
[self setDirty:YES];
[window flushWindow];
}
return self;
}
/*
* Sizes the selected objects to have their original sizes.
*/
- originalSize:sender
{
return [self makeOriginalUsing:@selector(setOriginalSize)];
}
/*
* Sizes the selected objects to have the save width/height ratio as their
* original bounding boxes. Centers them about their centers.
*/
- originalRatio:sender
{
return [self makeOriginalUsing:@selector(setOriginalRatio)];
}
/*
* Scrolls to rectangle passed in if it is not in visible portion of the view.
* If the rectangle is larger in width or height than the view, the scrollRectToVisible
* method is not altogether consistent. As a result, the rectangle contains only
* the image that was previously visible.
*/
- scrollToRect:(const NXRect *)toRect
{
NXRect visRect;
[self getVisibleRect:&visRect];
if (!NXContainsRect(&visRect, toRect))
{
[window disableFlushWindow];
[self scrollRectToVisible:toRect];
[window reenableFlushWindow];
startTimer(&timer, &timermask, window);
}
else
stopTimer(&timer, &timermask, window);
return self;
}
/*
* Constrain the point within the view. An offset is needed because when
* an object is moved, it is often grabbed in the center of the object. If the
* lower left offset and the upper right offset were not included then part of
* the object could be moved off of the view. (In some applications, that might
* be allowed but in this one the object is constrained to always lie in the
* page.)
*/
- constrainPoint:(NXPoint *)aPt withOffset:(const NXSize*)llOffset :(const NXSize*)urOffset
{
float margin;
NXPoint viewMin, viewMax;
margin = ceil(FONTSIZE/2);
viewMin.x = bounds.origin.x + llOffset->width + margin;
viewMin.y = bounds.origin.y + llOffset->height + margin;
viewMax.x = bounds.origin.x + bounds.size.width - urOffset->width - margin;
viewMax.y = bounds.origin.y + bounds.size.height - urOffset->height - margin;
aPt->x = MAX(viewMin.x, aPt->x);
aPt->y = MAX(viewMin.y, aPt->y);
aPt->x = MIN(viewMax.x, aPt->x);
aPt->y = MIN(viewMax.y, aPt->y);
return self;
}
/*
* Constrain a rectangle within the view.
*/
- constrainRect:(NXRect *)aRect
{
float margin;
NXPoint viewMin, viewMax;
margin = ceil(FONTSIZE/2);
viewMin.x = bounds.origin.x + margin;
viewMin.y = bounds.origin.y + margin;
viewMax.x = bounds.origin.x + bounds.size.width - aRect->size.width - margin;
viewMax.y = bounds.origin.y + bounds.size.height - aRect->size.height - margin;
aRect->origin.x = MAX(viewMin.x, aRect->origin.x);
aRect->origin.y = MAX(viewMin.y, aRect->origin.y);
aRect->origin.x = MIN(viewMax.x, aRect->origin.x );
aRect->origin.y = MIN(viewMax.y, aRect->origin.y);
return self;
}
/*
* Redraws the graphic. The image from the buffer is composited
* into the window and then the changed object is drawn atop the
* old image. A copy of the image is necessary because when the
* window is scrolled the buffer is also scrolled. When the
* buffer is scrolled, the old image might have to be redrawn.
* As a result, a copy is created and the changes performed on the
* copy. Care is taken to limit the amount of area that must be
* composited and redrawn. A timer is started is the scrolling rect
* moves outside the visible portion of the view.
*/
- redrawObject:objectId :(int) pt_num
{
id copyId;
BOOL tracking,
dirtyFlag = NO;
int old_mask;
float knobsize;
NXPoint pt, pt_last, pt_old, delta;
NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis;
NXEvent *event;
timermask = 0;
/*
* Create a copy of the selected object. If we scroll we will need to redraw
* the old object. If we do not create a copy we will not have an old object.
* The copy method copies the stream pointer and not the data
* if the imported file happens to be copied into the document.
*/
copyId = [objectId copyTemp];
knobsize = -[self controlPointSize]/2;
[copyId getBounds:&rect_start];
NXInsetRect(&rect_start, knobsize, knobsize);
rect_now = rect_last = rect_start;
/*
* The rect_scroll will cause scrolling whenever it goes outside the
* visible portion of the view.
*/
[copyId getScrollRect:&rect_scroll forPtNum:pt_num];
NXInsetRect(&rect_scroll, knobsize, knobsize);
[self getVisibleRect:&rect_vis];
NXIntersectionRect(&rect_vis, &rect_scroll);
[copyId getPoint:pt_num :&pt_last];
pt_old = pt_last;
old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
if (event->type != NX_MOUSEUP)
{
tracking = YES;
while (tracking)
{
/*
* If its a timer event than use the last point. It will be converted to
* the view's coordinate so it will appear as a new point.
*/
if (event->type == NX_TIMER)
pt = pt_old;
else
pt = pt_old = event->location;
[self convertPoint:&pt fromView:nil];
[copyId constrainPoint:&pt forPtNum:&pt_num
inRect:&bounds withFlags:event->flags];
delta.x = pt.x - pt_last.x;
delta.y = pt.y - pt_last.y;
if (delta.x || delta.y)
{
dirtyFlag = YES;
/* Change the point location and get the new bounds. */
[copyId setPoint:pt_num :&delta];
[copyId getBounds:&rect_now];
NXInsetRect(&rect_now, knobsize, knobsize);
/* Change the scrolling rectangle. */
NXOffsetRect(&rect_scroll, delta.x, delta.y);
[self scrollToRect:&rect_scroll];
/* Composite the old image and then redraw the new one. */
compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
[self drawObject:copyId forRect:&rect_now withFlags:REDRAWFLAG];
[self drawControl:copyId forRect:&rect_now withFlags:NOFLAGS];
/* Sync up the drawing so it proceeds a little smoother. */
[window flushWindow];
NXPing();
rect_last = rect_now;
pt_last = pt;
}
else
stopTimer(&timer, &timermask, window);
event = [NXApp getNextEvent:NX_MOUSEUPMASK|
NX_MOUSEDRAGGEDMASK|timermask];
tracking = (event->type != NX_MOUSEUP);
}
stopTimer(&timer, &timermask, window);
}
[window setEventMask:old_mask];
if (![graphiclistId replaceObject:objectId with:copyId])
[graphiclistId insertObject:copyId at:0];
if (![selectedlistId replaceObject:objectId with:copyId])
[selectedlistId insertObject:copyId at:0];
[objectId freeTemp];
if (dirtyFlag)
{
/*
* The view has already been focused and we know what
* has to be redrawn so call drawSelf:: instead of display
*/
NXUnionRect(&rect_last, &rect_start);
[self drawSelf:&rect_start :1];
[window flushWindow];
NXPing();
[self setDirty:YES];
}
return self;
}
/*
* Moves the selected objects by performing a translate before drawing
* the objects. This approach is used because the objects are drawn
* into windows and drawing simply means compositing the windows.
*
* The offsets constrain the selected object to stay within the dimensions
* of the view.
*/
- moveObject:(NXEvent *)event
{
BOOL tracking,
moveFlag = NO,
dirtyFlag = NO;
int old_mask;
long time;
float knobsize;
NXSize llOffset, urOffset;
NXPoint pt, pt_last, pt_old, delta, delta_scroll;
NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis;
/*
* Get the scrolling rectangle. Compare it against a reduced version of
* the visible rect. If it turns out to be the larger then use the reduced
* rectangle so that the user is not playing pong when trying to
* move the image.
*/
knobsize = -[self controlPointSize]/2;
[selectedlistId getScrollRect:&rect_scroll forPtNum:-1];
NXInsetRect(&rect_scroll, knobsize, knobsize);
[self getVisibleRect:&rect_vis];
NXInsetRect(&rect_vis, rect_vis.size.width * 0.20, rect_vis.size.height * 0.20);
if (rect_scroll.size.width > rect_vis.size.width || rect_scroll.size.height > rect_vis.size.height)
{
NXInsetRect(&rect_scroll, MAX(0, (rect_scroll.size.width - rect_vis.size.width)/2),
MAX(0, (rect_scroll.size.height - rect_vis.size.height)/2));
}
[selectedlistId getBounds:&rect_start];
NXInsetRect(&rect_start, knobsize, knobsize);
rect_now = rect_last = rect_start;
delta_scroll.x = rect_scroll.origin.x - rect_now.origin.x;
delta_scroll.y = rect_scroll.origin.y - rect_now.origin.y;
timermask = 0;
time = event->time;
pt_last = pt_old = event->location;
[self convertPoint:&pt_last fromView:nil];
/* Calculate where the mouse point falls relative to the object. */
llOffset.width = pt_last.x - rect_start.origin.x;
llOffset.height = pt_last.y - rect_start.origin.y;
urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x;
urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y;
/* Return nil if the the mouse was not dragged. */
old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
if (event->type != NX_MOUSEUP)
{
tracking = YES;
while (tracking)
{
/*
* Only move the object if a certain amount of time has elapsed
* between mouse down and mouse up. Prevents accidently
* moving objects when selecting.
*/
if (event->time - time > MOVE_INTERVAL)
{
moveFlag = YES;
if (event->type == NX_TIMER)
pt = pt_old;
else
pt = pt_old = event->location;
[self convertPoint:&pt fromView:nil];
[self constrainPoint:&pt withOffset:&llOffset :&urOffset];
[self constrainPoint:&pt_last withOffset:&llOffset :&urOffset];
delta.x = pt.x - pt_last.x;
delta.y = pt.y - pt_last.y;
if (delta.x || delta.y)
{
dirtyFlag = YES;
NXOffsetRect(&rect_now, delta.x, delta.y);
[self constrainRect:&rect_now];
rect_scroll.origin.x = rect_now.origin.x + delta_scroll.x;
rect_scroll.origin.y = rect_now.origin.y + delta_scroll.y;
[self scrollToRect:&rect_scroll];
/*
* Composite the old image into the window and then
* translate the user space and draw the objects.
*/
compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
PSgsave();
PStranslate(rect_now.origin.x - rect_start.origin.x,
rect_now.origin.y - rect_start.origin.y);
[self drawObject:selectedlistId forRect:&rect_start withFlags:MOVEFLAG];
[self drawControl:selectedlistId forRect:&rect_start
withFlags:NOFLAGS];
PSgrestore();
[window flushWindow];
NXPing();
rect_last = rect_now;
pt_last = pt;
}
else
stopTimer(&timer, &timermask, window);
}
event = [NXApp getNextEvent:NX_MOUSEUPMASK|
NX_MOUSEDRAGGEDMASK|timermask];
tracking = (event->type != NX_MOUSEUP);
}
stopTimer(&timer, &timermask, window);
if (dirtyFlag)
{
delta.x = rect_now.origin.x - rect_start.origin.x;
delta.y = rect_now.origin.y - rect_start.origin.y;
[selectedlistId moveAll:&delta];
/*
* The view has already been focused and we know what
* has to be redrawn so call drawSelf:: instead of display
*/
NXUnionRect(&rect_now, &rect_start);
[self drawSelf:&rect_start :1];
[window flushWindow];
NXPing();
[self setDirty:YES];
}
}
[window setEventMask:old_mask];
/*
* Return nil if the elapsed time is less than the amount necessary to
* consider the action a move.
*/
if (moveFlag)
return self;
else
return nil;
}
-eraseRotatePoint:(NXRect *) drawnRect
{
float scale;
NXRect rotateRect;
scale = 1.0 / [superview scale];
rotateRect.origin.x = rotatePoint.x - 8.0 * scale;
rotateRect.origin.y = rotatePoint.y - 8.0 * scale;
rotateRect.size.width = rotateRect.size.height = 16.0 * scale;
if (!NXContainsRect(drawnRect, &rotateRect))
{
compositeBuffer([bufferId gState], &rotateRect, &rotateRect.origin, NX_COPY);
[self drawControl:selectedlistId forRect:&rotateRect withFlags:NOFLAGS];
}
return self;
}
-drawRotatePoint
{
float scale;
NXPoint compPt;
scale = 1.0 / [superview scale];
compPt.x = rotatePoint.x - 8.0 * scale;
compPt.y = rotatePoint.y - 8.0 * scale;
[[[NXApp getCursor:OP_ROTATE1] image] composite:NX_SOVER toPoint:&compPt];
return self;
}
/*
* Mark the point about which to rotate. Employ gravity near any
* control points.
*/
- rotateObjectStart:(NXEvent *)event
{
id objectId;
int pt_num;
rotatePoint = event->location;
[self convertPoint:&rotatePoint fromView:nil];
[self lockFocus];
if (objectId = [self checkControl:&rotatePoint :&pt_num])
[objectId getPoint:pt_num :&rotatePoint];
[self drawRotatePoint];
[self unlockFocus];
[window flushWindow];
return self;
}
/*
* Rotates the selected graphics about the rotatePoint selected
* from the previous mouse down. The image from the buffer
* is composited into the window and then the rotated object is
* drawn atop the old image.
*
* Care is taken to limit the amount of area that must be
* composited and redrawn. A timer is started is the scrolling rect
* moves outside the visible portion of the view.
*/
- rotateObject:(NXEvent *)event
{
id slist;
BOOL tracking,
dirtyFlag = NO;
int old_mask;
float knobsize, marginsize;
float radians_start, radians_last, radians_delta;
NXPoint pt, pt_old, d;
NXRect rect_now, rect_start, rect_last, rect_scroll;
slist = [selectedlistId copyTemp];
timermask = 0;
marginsize = (1.0/[superview scale] * SCROLL_MARGIN)/2;
rect_scroll.size.width = rect_scroll.size.height = marginsize * 2;
knobsize = -[self controlPointSize]/2;
[slist getBounds:&rect_start];
NXInsetRect(&rect_start, knobsize, knobsize);
rect_now = rect_last = rect_start;
pt_old = pt = event->location;
[self convertPoint:&pt fromView:nil];
d.x = pt.x - rotatePoint.x;
d.y = pt.y - rotatePoint.y;
radians_start = 0;
if (d.x || d.y)
radians_start = atan2(d.y,d.x);
radians_last = radians_start;
[self lockFocus];
old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
if (event->type != NX_MOUSEUP)
{
tracking = YES;
while (tracking)
{
/*
* If its a timer event than use the last point. It will be converted to
* the view's coordinate so it will appear as a new point.
*/
if (event->type == NX_TIMER)
pt = pt_old;
else
pt = pt_old = event->location;
[self convertPoint:&pt fromView:nil];
d.x = pt.x - rotatePoint.x;
d.y = pt.y - rotatePoint.y;
if (d.x || d.y)
{
dirtyFlag = YES;
radians_delta = atan2(d.y, d.x) - radians_last;
[slist constrainAngle:&radians_delta withFlags:event->flags];
/* Rotate the object about the given point at the given angle. */
[slist rotateAboutPoint:&rotatePoint withAngle:radians_delta];
[slist getBounds:&rect_now];
NXInsetRect(&rect_now, knobsize, knobsize);
/* Make the scrolling rectangle the mouse point with a little margin. */
rect_scroll.origin.x = pt.x - marginsize;
rect_scroll.origin.y = pt.y - marginsize;
[self scrollToRect:&rect_scroll];
/* Composite the old image and then redraw the new one. */
NXIntegralRect(&rect_last);
compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
[self drawObject:slist forRect:&rect_now withFlags:REDRAWFLAG];
[self drawControl:slist forRect:&rect_now withFlags:NOFLAGS];
[self drawRotatePoint];
/* Sync up the drawing so it proceeds a little smoother. */
[window flushWindow];
NXPing();
rect_last = rect_now;
radians_last = radians_delta + radians_last;
}
else
stopTimer(&timer, &timermask, window);
event = [NXApp getNextEvent:NX_MOUSEUPMASK|
NX_MOUSEDRAGGEDMASK|timermask];
tracking = (event->type != NX_MOUSEUP);
}
stopTimer(&timer, &timermask, window);
}
[window setEventMask:old_mask];
[graphiclistId replaceObjectsIn:selectedlistId with:slist];
[selectedlistId freeTemp];
[selectedlistId free];
selectedlistId = slist;
if (dirtyFlag)
{
/*
* The view has already been focused and we know what
* has to be redrawn so call drawSelf:: instead of display
*/
NXUnionRect(&rect_last, &rect_start);
[self drawSelf:&rect_start :1];
[self eraseRotatePoint:&rect_start];
[window flushWindow];
NXPing();
[self setDirty:YES];
}
[self unlockFocus];
return self;
}
/* Check to see whether a control point has been hit. */
- checkControl:(const NXPoint *) p :(int *) pt_num
{
float hitsetting;
NXRect hitRect;
hitsetting = [self hitSetting];
NXSetRect(&hitRect, p->x - hitsetting/2, p->y - hitsetting/2, hitsetting, hitsetting);
return [selectedlistId hitControl:&hitRect :pt_num forSize:[self controlPointSize]];
}
/*
* Check to see whether an object has been hit. Return the selected list if
* any object in it has been hit. Otherwise just return the individual
* graphic.
*/
- checkObject:(const NXPoint *) p in:listId from:(int) start to:(int) end
{
id objectId;
BOOL flag;
int i;
float hitsetting;
hitsetting = [self hitSetting];
/* Bounding Box */
hitPoint->pts[0] = floor(p->x - hitsetting/2);
hitPoint->pts[1] = floor(p->y - hitsetting/2);
hitPoint->pts[2] = ceil(p->x + hitsetting/2);
hitPoint->pts[3] = ceil(p->y + hitsetting/2);
/* Moveto */
hitPoint->pts[4] = p->x - hitsetting/2;
hitPoint->pts[5] = p->y - hitsetting/2;
/* Rlineto's */
hitPoint->pts[7] = hitsetting;
hitPoint->pts[8] = hitsetting;
hitPoint->pts[11] = -hitsetting;
flag = (listId == graphiclistId);
for (i = start; i < end; i++)
if (objectId = [[listId objectAt:i] hitObject:hitPoint ifNotSelected:flag])
return objectId;
return nil;
}
/*
* Set the object as selected, add the object to the selected list and
* then draw the control points.
*/
- selectObject:objectId
{
NXRect drawRect;
[objectId setSelected:YES];
[selectedlistId addObject:objectId];
[self getVisibleRect:&drawRect];
[self drawControl:objectId forRect:&drawRect withFlags:NOFLAGS];
[window flushWindow];
return self;
}
/*
* Remove the object (or objects) from the selected list and redraw the
* control points for the visible portion. The control points are drawn
* in this view since they can be drawn pretty quickly (and so the don't
* need to be cached in an offscreen buffer somewhere).
*/
- deselectObject:objectId
{
NXRect drawRect;
[self getVisibleRect:&drawRect];
[objectId setSelected:NO];
if (objectId == selectedlistId)
[selectedlistId empty];
else
[selectedlistId removeObject:objectId];
[self drawControl:selectedlistId forRect:&drawRect withFlags:CLEARFLAG];
[window flushWindow];
return self;
}
/*
* Test for a mouse down hit on either the object or the control points.
* This algorithm looks further into the list in order to check for mouse
* hits on objects that lie below the currently selected object.
*/
- testObject:(NXEvent *)event
{
id objectId, newobjectId;
int pt_num, list_index;
NXPoint p;
p = event->location;
[self convertPoint:&p fromView:nil];
[self lockFocus];
if (objectId = [self checkControl:&p :&pt_num])
{
[self redrawObject:objectId :pt_num];
}
else
{
if ([selectedlistId count] == 0 ||
(event->flags & NX_SHIFTMASK) == NX_SHIFTMASK)
{
if (objectId = [self checkObject:&p in:selectedlistId
from:0 to:[selectedlistId count]])
[self deselectObject:objectId];
else if (objectId = [self checkObject:&p in:graphiclistId
from:0 to:[graphiclistId count]])
[self selectObject:objectId];
}
else
{
objectId = [self checkObject:&p in:selectedlistId
from:0 to:[selectedlistId count]];
if (!objectId || ![self moveObject:event])
{
/* Look further into the list first. */
list_index = 0;
if (objectId)
list_index = [graphiclistId indexOf:objectId];
if ((newobjectId = [self checkObject:&p in:graphiclistId
from:list_index to:[graphiclistId count]]) ||
(newobjectId = [self checkObject:&p in:graphiclistId
from:0 to:list_index]))
{
[self deselectObject:selectedlistId];
[self selectObject:newobjectId];
}
else if (!objectId)
[self deselectObject:selectedlistId];
}
}
}
[self unlockFocus];
return self;
}
/*
* Free any previously unplaced imported object first. Next,
* pass the file name to the factory Tiff or Epsf object to create a new
* instance. If successful then remove items from the selected list.
*
* If a point has been passed in then place the imported file at its
* original size with its left corner corresponding to p. If not
* then set the NXApp operation to OP_PLACE. The next mouse
* down will begin the modal loop for drawing out the sizing rectangle.
*/
- importFile:(const char *) file at:(NXPoint *) p
{
BOOL ok;
char *end;
NXStream *stream;
ok = NO;
[graphicId free];
graphicId = NULL;
if (file)
{
end = strrchr(file, '.');
if (end)
{
if ([[ImportPanel new] format] == IMPORT_COPY)
{
stream = NXMapFile(file, NX_READONLY);
if (stream)
{
if (strncmp(end, ".tiff", 5) == 0 ||
strncmp(end, ".ps", 3) == 0 ||
strncmp(end, ".eps", 4) == 0)
{
graphicId = [[GraphicImport alloc] initFromStream:stream];
[graphicId setFilename:file];
}
NXCloseMemory(stream, NX_SAVEBUFFER);
}
else
Notify("Import Error", "Unable to open file.");
}
else
{
if (strncmp(end, ".tiff", 5) == 0 ||
strncmp(end, ".ps", 3) == 0 ||
strncmp(end, ".eps", 4) == 0)
{
graphicId = [[GraphicImport alloc] initFromFile:file];
}
}
if (graphicId)
{
[self lockFocus];
[self deselectObject:selectedlistId];
[self unlockFocus];
if (p)
{
[self placeObjectAt:p];
graphicId = NULL;
}
else
[NXApp setOperation:OP_IMPORT];
[window flushWindow];
NXPing();
ok = YES;
}
}
else
Notify("Import Error", "Unable to import file. Unrecognized file type.");
}
return ok ? self : nil;
}
/*
* Place the graphicId with its upper left corner at p;
*/
- placeObjectAt:(const NXPoint *) p
{
float knobsize;
NXPoint pt;
NXRect rect_draw;
if (graphicId)
{
pt = *p;
[graphiclistId insertObject:graphicId at:0];
[selectedlistId insertObject:graphicId at:0];
[graphicId getBounds:&rect_draw];
pt.y = pt.y - rect_draw.size.height;
[graphicId setOrigin:&pt];
knobsize = -[self controlPointSize]/2;
[graphicId getBounds:&rect_draw];
NXInsetRect(&rect_draw, knobsize, knobsize);
[self display:&rect_draw :1];
}
return self;
}
/*
* Begins the setup for placing an imported file into the document.
* The object for the file is created and then waits for the mouse
* down to begin placement and sizing. This method first gets the
* object and then messages the redrawObject method to draw
* the subsequent sizing rectangles.
*/
- importObject:(NXEvent *)event
{
float knobsize;
NXPoint p;
NXRect rect_draw;
if (graphicId)
{
p = event->location;
[self convertPoint:&p fromView:nil];
if ([[ImportPanel new] dragToSize])
{
[NXApp setOperation:OP_PLACE];
NXSetRect(&rect_draw, p.x, p.y-SIZE_MIN, SIZE_MIN, SIZE_MIN);
[graphicId setBounds:&rect_draw];
knobsize = -[self controlPointSize]/2;
NXInsetRect(&rect_draw, knobsize, knobsize);
[self lockFocus];
[self drawObject:graphicId forRect:&rect_draw withFlags:REDRAWFLAG];
[self drawControl:graphicId forRect:&rect_draw withFlags:NOFLAGS];
[self redrawObject:graphicId :8];
[self unlockFocus];
graphicId = [selectedlistId objectAt:0];
}
else
[self placeObjectAt:&p];
if ([graphicId error])
{
[self lockFocus];
[self deselectObject:graphicId];
[self unlockFocus];
[graphiclistId removeObject:graphicId];
[graphicId free];
}
else
[self setDirty:YES];
graphicId = NULL;
}
return self;
}
/*
* Depending on the current operation, check for selection, zoom or
* import a file.
*/
- mouseDown:(NXEvent *)event
{
int operation;
operation = [NXApp operation];
switch (operation)
{
case OP_SELECT:
[self testObject:event];
break;
case OP_ZOOMUP:
case OP_ZOOMDOWN:
[nextResponder scaleDrawView:self withEvent:event];
break;
case OP_ROTATE1:
[self rotateObjectStart:event];
[NXApp setOperation:OP_ROTATE2];
break;
case OP_ROTATE2:
[self rotateObject:event];
[NXApp setOperation:OP_ROTATE1];
break;
case OP_IMPORT:
[self importObject:event];
[NXApp clearOperation];
break;
}
return self;
}
/*
* Changes the cursor to the reduce cursor if the shift key is
* pressed when zooming.
*/
- flagsChanged:(NXEvent *) event
{
BOOL shift;
int operation;
operation = [NXApp operation];
shift = (event->flags & NX_ALTERNATEMASK) == NX_ALTERNATEMASK;
if (operation == OP_ZOOMUP && shift)
[NXApp setOperation:OP_ZOOMDOWN];
else if (operation == OP_ZOOMDOWN && !shift)
[NXApp setOperation:OP_ZOOMUP];
else
return [super flagsChanged:event];
return self;
}
/*
* Deletes the selected objects.
*/
- keyDown:(NXEvent *) event
{
if ([NXApp operation] == OP_SELECT &&
event->data.key.charSet == NX_ASCIISET &&
event->data.key.charCode == NX_DELETE)
return [self cut:self];
else
return nil;
}
/*
* Draw the control points using the user path buffer to hold
* the data for the xyshow. Having the object fill in the data
* structure allows for combining the control points for multiple
* objects. This increases drawing performance by reducing
* the number of operations. But since this operation is performed
* after the objects have been drawn, the control points are above
* the graphics. This approach may or may not be desired.
*/
- drawControl:object forRect:(NXRect *)r withFlags:(int)flags
{
float knobsize;
NXPoint lastpoint;
NXRect rect;
if (r)
rect = *r;
else
[self getVisibleRect:&rect];
if (flags & CLEARFLAG)
compositeBuffer([bufferId gState], &rect, &rect.origin, NX_COPY);
lastpoint.x = 0;
lastpoint.y = 0;
upathBuffer->num_ops = 0;
upathBuffer->num_pts = 0;
knobsize = [self controlPointSize];
NXInsetRect(&rect, -knobsize/2, -knobsize/2);
[object putControlPoints:upathBuffer forRect:&rect :&lastpoint];
upathBuffer->ops[upathBuffer->num_ops] = 0;
upathBuffer->pts[upathBuffer->num_pts] = 0;
upathBuffer->pts[upathBuffer->num_pts + 1] = 0;
if (upathBuffer->num_ops > 0)
{
PSWSetControlPoints(ControlFont, knobsize, NX_BLACK, 0.15);
PSWDrawControlPoints(upathBuffer->pts[0], upathBuffer->pts[1],
&upathBuffer->pts[2], upathBuffer->num_pts, upathBuffer->ops);
}
return self;
}
- drawObject:object forRect:(NXRect *)r withFlags:(int) flags
{
NXRect rect;
if (r)
rect = *r;
else
[self getVisibleRect:&rect];
[object drawObject:&rect withFlags:flags inView:self];
return self;
}
/*
* Fill in the background of the rectangle and then draw the graphics.
* If NX_DRAWING, then draw into the buffer and composite into
* this view's window.
*/
- drawSelf:(NXRect *)r :(int) count
{
if (NXDrawingStatus == NX_DRAWING)
{
[bufferId lockFocus];
PSsetgray(NX_WHITE);
NXRectFill(r);
[bufferId unlockFocus];
compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
[bufferId lockFocus];
}
[self drawObject:graphiclistId forRect:r withFlags:REFRESHFLAG];
if (NXDrawingStatus == NX_DRAWING)
{
[bufferId unlockFocus];
compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
[self drawControl:selectedlistId forRect:r withFlags:NOFLAGS];
}
return self;
}
/*
* This method is only overridden to eliminate during a copy
* the rectclip and gsave/grestore pairing that results from
* a lockFocus. These are usually harmless operations but
* they interfere with trying to produce Illustrator format files.
*/
- display:(NXRect *)r :(int) count :(BOOL)flag
{
if (NXDrawingStatus == NX_COPYING)
{
[self drawSelf:r :count];
DPSFlushContext(DPSGetCurrentContext());
}
else
[super display:r :count :flag];
return self;
}
/*
* Do not focus if Printing because we don't want the
* scale factored in.
*/
- (BOOL) lockFocus
{
if (NXDrawingStatus != NX_PRINTING)
return [super lockFocus];
return YES;
}
- unlockFocus
{
if (NXDrawingStatus != NX_PRINTING)
return [super unlockFocus];
return self;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- write:(NXTypedStream *)stream
{
[super write:stream];
NXWriteTypes(stream, "@", &graphiclistId);
return self;
}
- read:(NXTypedStream *)stream
{
[super read:stream];
NXReadTypes(stream, "@", &graphiclistId);
return self;
}
- awake
{
selectedlistId = [GraphicList new];
hitPoint = [NXApp hitPoint];
upathBuffer = [NXApp upathBuffer];
bufferId = createBuffer(&DefaultContentRect.size);
return self;
}
/*
* Validates menu commands. It returns NO if the
* DrawingView knows that action is not valid now,
* otherwise it returns YES.
*
* Using pastecount and pastevalid prevents having to look into
* the Pasteboard every time paste: is validated.
*/
- (BOOL)validateCommand:menuCell
{
SEL action = [menuCell action];
id pasteboardId;
int count;
if ( action == @selector(cut:) ||
action == @selector(delete:) ||
action == @selector(copy:) ||
action == @selector(bringToFront:) ||
action == @selector(sendToBack:) ||
action == @selector(originalSize:) ||
action == @selector(originalRatio:))
{
return([selectedlistId count] > 0);
}
else if (action == @selector(selectAll:))
{
return([graphiclistId count] > 0);
}
else if (action == @selector(paste:))
{
pasteboardId = [Pasteboard new];
count = [pasteboardId changeCount];
if (count != pastecount)
{
pastecount = count;
pastevalid = (drawPasteType([pasteboardId types]) != NULL);
}
return pastevalid;
}
return YES;
}
- (BOOL) knowsPagesFirst:(int *) firstPageNum last:(int *) lastPageNum
{
*firstPageNum = 1;
*lastPageNum = 1;
return YES;
}
- (BOOL) getRect:(NXRect *) theRect forPage:(int) page
{
if (page == 1)
{
*theRect = bounds;
return YES;
}
return NO;
}
/*
* Used when printing. Returns the global resources used in the document.
*/
- addResources:(Resource *) resourceDoc
{
NXAtom string;
if (NXDrawingStatus == NX_COPYING)
{
string = NXUniqueString(EpsfProcSet);
if (!resourceDoc[RES_PROCSETS].states[RES_PRESENT])
resourceDoc[RES_PROCSETS].states[RES_PRESENT] = [List new];
[resourceDoc[RES_PROCSETS].states[RES_PRESENT]
addObjectIfAbsent:(id) string];
if (!resourceDoc[RES_PROCSETS].states[RES_SUPPLIED])
resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] = [List new];
[resourceDoc[RES_PROCSETS].states[RES_SUPPLIED]
addObjectIfAbsent:(id) string];
}
return self;
}
/*
* Print the %%DocumentResource comments. A list of the resources is
* accumulated from the imported files (only one in this case).
* The list is then written to the current context.
*/
- beginResourceComments:(const NXRect *) bbox
{
int i, j;
Resource resourceDoc[RES_NUMTYPES];
bzero(&resourceDoc, sizeof(resourceDoc));
[self addResources:resourceDoc];
[graphiclistId addResources:resourceDoc for:(NXRect *) bbox];
for (i = 0; i < RES_NUMTYPES; i++)
{
for (j = 0; j < RES_NUMSTATES; j++)
{
if (resourceDoc[i].states[j])
{
WriteEpsfResource(resourceDoc[i].states[j], i, j);
[resourceDoc[i].states[j] free];
}
}
}
return self;
}
/*
* Write out the necessary information. Overridden to include
* the fonts from the EPSF files.
*/
- beginPrologueBBox:(const NXRect *)boundingBox
creationDate:(const char *)dateCreated
createdBy:(const char *)anApplication
fonts:(const char *)fontNames
forWhom:(const char *)user
pages:(int)numPages
title:(const char *)aTitle
{
time_t clock;
DPSContext ctxt;
ctxt = DPSGetCurrentContext();
if (!boundingBox)
boundingBox = [[NXApp printInfo] paperRect];
if (!dateCreated)
{
clock = time(0);
dateCreated = ctime(&clock);
}
if (!anApplication)
anApplication = [NXApp appName];
if (!user)
user = (char *) getlogin();
if (numPages <= 0)
numPages = 1;
if (!aTitle)
aTitle = [[window delegate] filename];
DPSPrintf(ctxt, "%%!PS-Adobe-2.0 EPSF-1.2\n");
DPSPrintf(ctxt, "%%%%Creator: %s\n", anApplication);
DPSPrintf(ctxt, "%%%%For: %s\n", user);
DPSPrintf(ctxt, "%%%%Title: %s\n", aTitle);
DPSPrintf(ctxt, "%%%%CreationDate: %s", dateCreated);
DPSPrintf(ctxt, "%%%%BoundingBox: %d %d %d %d\n", (int) floor(boundingBox->origin.x),
(int) floor(boundingBox->origin.y),
(int) ceil(boundingBox->origin.x + boundingBox->size.width),
(int) ceil(boundingBox->origin.y + boundingBox->size.height));
if (NXDrawingStatus == NX_COPYING)
DPSPrintf(ctxt, "%%AI3_TemplateBox: %d %d %d %d\n", 306, 396, 306, 396);
if (NXDrawingStatus != NX_COPYING)
DPSPrintf(ctxt, "%%%%Pages: %d\n", numPages);
[self beginResourceComments:boundingBox];
if ([[NXApp printInfo] orientation] == NX_LANDSCAPE)
DPSPrintf(ctxt, "%%%%Orientation: Landscape\n");
else
DPSPrintf(ctxt, "%%%%Orientation: Portrait\n");
return self;
}
/*
* Includes the abbreviated Illustrator proc set so that
* the imported files produced through Save To will print on their
* own. Also includes the preview data as a comment when
* specified.
*/
- endHeaderComments
{
DPSContext ctxt;
if (NXDrawingStatus == NX_COPYING)
{
ctxt = DPSGetCurrentContext();
DPSPrintf(ctxt, "%%%%EndComments\n\n");
DPSPrintf(ctxt, "%%%%BeginProcSet: EPSF_Illustrator_abbrev 0 0\n");
WriteEpsfProcSetDef ();
DPSPrintf(ctxt, "%%%%EndProcSet\n\n");
if (imageId && [[SaveAsPanel new] format] == SAVE_EPSPREVIEW)
WriteEpsfPreview(imageId);
}
else
[super endHeaderComments];
return self;
}
/*
* If saving in illustrator, override the prologue comment.
*/
- endPrologue
{
DPSContext ctxt;
if (NXDrawingStatus != NX_COPYING)
{
ctxt = DPSGetCurrentContext();
DPSPrintf(ctxt, "%%%%EndProlog\n\n");
}
else
[super endPrologue];
return self;
}
/* Initialize the Illustrator abbreviated proc set. */
- beginSetup
{
DPSContext ctxt;
if (NXDrawingStatus == NX_COPYING)
{
ctxt = DPSGetCurrentContext();
DPSPrintf(ctxt, "%%%%BeginSetup\n");
WriteEpsfProcSetInit();
}
else
[super beginSetup];
return self;
}
- endSetup
{
DPSContext ctxt;
if (NXDrawingStatus == NX_COPYING)
{
ctxt = DPSGetCurrentContext();
DPSPrintf(ctxt, "%%%%EndSetup\n");
}
else
[super endSetup];
return self;
}
/* Terminate the Illustrator abbreviated proc set. */
- beginTrailer
{
DPSContext ctxt;
if (NXDrawingStatus == NX_COPYING)
{
ctxt = DPSGetCurrentContext();
DPSPrintf(ctxt, "%%%%Trailer\n");
WriteEpsfProcSetTerm();
}
else
[super beginTrailer];
return self;
}
- endTrailer
{
if (NXDrawingStatus != NX_COPYING)
[super endTrailer];
return self;
}
@end